package Datenbank;

import java.sql.*;
import java.util.*;
import Applikationslogik.*;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Bildet Objekte des Typs Kalendereintrag auf der relationalen Datenbank ab.
 * Die Klasse beinhaltet Methoden und Objekte zum Anlegen, Ändern, Löschen und Suchen.
 *
 * @author Daniel Mossiossian & René van Straaten
 */

public class KalendereintragMapper {

    private static KalendereintragMapper kalendereintragMapper = null;

    /**
     * Stellt sicher, dass nur eine Istanz von KalendereintragMapper erzeugt wird.
     *
     * @return kalendereintragMapper das kalendereintragMapper- Objekt
     */
    public static KalendereintragMapper KalendereintragMapper() {
        if (kalendereintragMapper == null) {
            kalendereintragMapper = new KalendereintragMapper();
        }
        return kalendereintragMapper;
    }

    /**
    * Fügt ein neues Kalendereintrag- Objekt in die Datenbank ein.
    * Dabei wird auch überprüft was die bisher höhste eintrag_id (der Primärschlüssel) ist.
    *
    * @param ke das zu speichernde Kalendereintrag- Objekt
    * @return das bereits übergebene Objekt, jedoch mit ggf. korrigierter eintrag_id vom Typ Kalendereintrag
    */
    public Kalendereintrag insert(Kalendereintrag ke) {
        Connection con = DBConnection.connection();

        try {
            Statement stmt = con.createStatement();

            /*
             * Hier wird überprüft welche fluglinie_id bisher die höhste ist.
             */
            ResultSet rs = stmt.executeQuery(
                    "SELECT MAX(eintrag_id) AS maxid " +
                    "FROM Kalendereintrag");


            if (rs.next()) {
                try {
                    // Hier wird die bisher höhste vorhandene eintrag_id um 1 erhöht.

                    ke.set_eintrag_id(rs.getInt("maxid") + 1);

                    stmt = con.createStatement();

                    /* Die tatsächliche Einfügeoperation mit den übergebenden Attributen.
                     */

                    stmt.executeUpdate("INSERT INTO Kalendereintrag (eintrag_id, typ, erinnerung, ort, start, ende, beschreibung, kategorie_id, person_id) VALUES (" +
                            ke.get_eintrag_id() + ",'" +
                            "k" + "'," +
                            ke.get_erinnerung() + ",'" +
                            ke.get_ort() + "','" +
                            ke.get_start() + "','" +
                            ke.get_ende() + "','" +
                            ke.get_beschreibung() + "'," +
                            ke.get_kategorie() + "," +
                            ke.get_erstelltvon() + ")");



                    stmt.executeUpdate("INSERT INTO Teilnahme (kalender_id, eintrag_id, teilnahme) VALUES (" +
                            ke.get_kalender_id() + "," +
                            ke.get_eintrag_id() + "," +
                            "1" + ")");


                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }


        // Rückgabe des angelegten Objekts ke von Typ Kalendereintrag

        return ke;
    }

    /**
     * Aktualisiert das übergebene Kalendereintrag- Objekt in der Datenbank.
     *
     * @param ke das zu aktualisierende Kalendereintrag- Objekt
     * @return ke das als Parameter übergebende Objekt des Typs Kalendereintrag
     */
    public Kalendereintrag update(Kalendereintrag ke) {
        /* Stellt durch Aufruf der connection() Methode der DBConnection-Klasse, die Verbindung zur Datenbank her. */
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

                   stmt.executeUpdate("UPDATE Kalendereintrag " +
                    "SET eintrag_id=" + ke.get_eintrag_id() + ", " +
                    "typ='k', " +
                    "erinnerung=" + ke.get_erinnerung() + ", " +
                    "ort='" + ke.get_ort() + "', " +
                    "start='" + ke.get_start() + "', " +
                    "ende='" + ke.get_ende() + "', " +
                    "beschreibung='" + ke.get_beschreibung() + "', " +
                    // "wiederholung='null', " +
                    // "enddatum='null', " +
                    "kategorie_id=" + ke.get_kategorie() + ", " +
                    "person_id=" + ke.get_erstelltvon() + " " +
                    "WHERE eintrag_id=" + ke.get_eintrag_id());
        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        // Das als Parameter übergebende Objekt des Typs Kalendereintrag wird zurückgegeben
        return ke;
    }

    /**
     * Löscht alle Daten eines Kalendereintrag- Objekts aus der Datenbank.
     *
     * @param ka das zu löschende Objekt vom Typ Kalendereintrag
     */
    public void delete(Kalendereintrag ke) {
        Connection con = DBConnection.connection();
        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

            // Löschen in der Tabelle Kalendereintrag
            stmt.executeUpdate("DELETE FROM Kalendereintrag " +
                    "WHERE eintrag_id=" + ke.get_eintrag_id());

            // Löschen in der Tabelle Teilnahme
            stmt.executeUpdate("DELETE FROM Teilnahme " +
                    "WHERE eintrag_id=" + ke.get_eintrag_id());

        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Liest alle Personen aus.
     *
     * @return result Vektor mit Personen- Objekten, d.h. allen in der Datenbank angelegte Kalendereinträge, zurückgegeben.
     * Bei evtl. Exceptions wird ein partiell gefüllter oder ggf. auch leerer Vektor vom Typ Kalendereintrag zurückgeliefert.
     */
    public Vector findall_person() {
        Connection con = DBConnection.connection();
        Vector result = new Vector();

        try {
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery(
                    "SELECT * " +
                    "FROM Person " +
                    "ORDER BY person_id");

            // Für jeden Eintrag im Suchergebnis wird nun ein Kalendereintrag- Objekt ke erstellt.
            while (rs.next()) {
                try {
                    PersonImpl pe = new PersonImpl();
                    pe.set_person_id(rs.getInt("person_id"));
                    pe.set_raum(rs.getInt("raum"));
                    pe.set_nachname(rs.getString("nachname"));
                    pe.set_vorname(rs.getString("vorname"));
                    pe.set_telefon(rs.getInt("telefon"));
                    pe.set_ist_admin(rs.getBoolean("ist_admin"));
                    pe.set_email(rs.getString("email"));
                    result.addElement(pe);
                    // Wenn kein Titel vorhanden ist bleibt der String Titel des Vectors leer
                } catch (Exception e) {
                    e.printStackTrace();
                    //Bei einer evtl. Exception wird die while-Schleife abgebrochen.
                    break;
                }

            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        return result;
    }

    /**
     * Sucht die Datenbank nach einem Kalendereintrag mit der eingegebenen ID.
     *
     * @param eintrag_id ist das Primärschlüsselattribut Kalendereintrag in der Datenbank.
     * @return ka Objekt vom Typ Kalendereintrag, das der id entspricht wird zurückgegebn.
     * null wird zurückgegeben, wenn das Objekt nicht in der Datenbank vorhanden ist.
     * Da es sich um einen Primärschlüssel handelt und dieser somit eindeutig ist, wird genau ein Objekt zurückgegeben.
     */
    public Kalender findbyid(int id) {
        Connection con = DBConnection.connection();

        try {
            Statement stmt = con.createStatement();

            ResultSet rs = stmt.executeQuery(
                    "SELECT * FROM Kalender " +
                    "WHERE kalender_id =" + id);

            /*
             * Da es sich bei id um einen Primärschlüssel handelt, kann max. nur ein Tupel zurückgegeben werden.
             * Es wird geprüft, ob ein Ergebnis vorliegt. Wenn ja werden die Attribute eintrag_id,
             * und bezeichnung ausgelesen und als Typ Kalendereintrag zurückgegeben.
             */
            if (rs.next()) {
                try {
                    System.out.println(id);
                    KalenderImpl ka = new KalenderImpl();
                    ka.set_kalender_id(rs.getInt("kalender_id"));
                    ka.set_bezeichnung(rs.getString("bezeichnung"));
                    ka.set_besitzer(rs.getInt("besitzer"));

                    try {
                        Statement stmt2 = con.createStatement();

                        ResultSet rs2 = stmt2.executeQuery(
                                "SELECT person_id FROM Berechtigung " +
                                "WHERE kalender_id =" + id + " AND berechtigung='r'");

                        /*
                         * Da es sich bei id um einen Primärschlüssel handelt, kann max. nur ein Tupel zurückgegeben werden.
                         * Es wird geprüft, ob ein Ergebnis vorliegt. Wenn ja werden die Attribute person_id,
                         * ausgelesen und als Typ Kalendereintrag zurückgegeben.
                         */

                        List<Integer> personenliste_lesen = new ArrayList<Integer>();
                        System.out.println("Es folgen die ausgelesenen Leseberechtigungen:");
                        while (rs2.next()) {
                            try {
                                System.out.println(rs2.getInt("person_id"));
                                personenliste_lesen.add(rs2.getInt("person_id"));
                                ka.set_berechtigung_lesen(personenliste_lesen);

                            } catch (Exception e) {
                                e.printStackTrace();
                                return null;
                            }
                        }

                    } catch (SQLException e2) {
                        e2.printStackTrace();
                        return null;
                    }



                    try {
                        Statement stmt3 = con.createStatement();

                        ResultSet rs3 = stmt3.executeQuery(
                                "SELECT person_id FROM Berechtigung " +
                                "WHERE kalender_id =" + id + " AND berechtigung='w'");

                       

                        List<Integer> personenliste_schreiben = new ArrayList<Integer>();
                        System.out.println("");
                        System.out.println("Es folgen die ausgelesenen Schreibberechtigungen:");
                        while (rs3.next()) {
                            try {
                                System.out.println(rs3.getInt("person_id"));
                                personenliste_schreiben.add(rs3.getInt("person_id"));
                                ka.set_berechtigung_schreiben(personenliste_schreiben);
                            }

                            catch (Exception e) {
                                e.printStackTrace();
                                return null;
                            }
                        }


                        return ka;

                    } catch (SQLException e2) {
                        e2.printStackTrace();
                        return null;
                    }




                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }

            }

        } catch (SQLException e2) {
            e2.printStackTrace();
            return null;
        }

        return null;
    }

    /**
     *
     *
     * @param person_id
     * @return result Ergebnisvektor vom Typ Kalendereintrag
     */
    public Vector <Kalendereintrag> findall_kalendereintraege_person(int person_id) {
        Connection con = DBConnection.connection();
        Vector result = new Vector();

        try {
            Statement stmt = con.createStatement();


            ResultSet rs = stmt.executeQuery(

                    "SELECT * " +
                    "FROM Kalendereintrag " +
                    "WHERE typ='k' AND person_id=" + person_id);

            // Für jeden Eintrag im Suchergebnis wird nun ein Kalendereintrag-Objekt ke erstellt.
            while (rs.next()) {
                try {
                    Kalendereintrag ke = new KalendereintragImpl();
                    ke.set_beschreibung(rs.getString("beschreibung"));
                    ke.set_eintrag_id(rs.getInt("eintrag_id"));
                    ke.set_ende(rs.getTimestamp("ende"));
                    ke.set_erinnerung(rs.getInt("erinnerung"));
                    ke.set_erstelltvon(rs.getInt("person_id"));
                    ke.set_kalender_id(0); //Setzen wir hier in dem Fall nicht, da dieser keine Rolle spielt
                    ke.set_kategorie(rs.getInt("kategorie_id"));
                    ke.set_ort(rs.getString("ort"));
                    ke.set_start(rs.getTimestamp("start"));
                    result.addElement(ke);

                    // Wenn kein Titel vorhanden ist bleibt der String Titel des Vectors leer
                } catch (Exception e) {
                    e.printStackTrace();
                    //Bei einer evtl. Exception wird die while-Schleife abgebrochen.
                    break;
                }

            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Kalendereintrag wird zurückgeben.
        return result;

    }

    /**
     *
     *
     * @param start
     * @param ende
     * @param kalender_id
     * @return result Ergebnisvektor vom Typ Kalendereintrag
     */
    public boolean check_datum_available(Timestamp start, Timestamp ende, int kalender_id) {
        Connection con = DBConnection.connection();
        boolean result = false;

        try {
            Statement stmt = con.createStatement();



            ResultSet rs = stmt.executeQuery(

            "SELECT COUNT(*) AS count FROM Kalendereintrag INNER JOIN Teilnahme ON Kalendereintrag.eintrag_id = Teilnahme.eintrag_id WHERE kalender_id=" + kalender_id + " AND ( ('" +
                    start + "' BETWEEN Kalendereintrag.start AND Kalendereintrag.ende ) OR ('" +
                    ende + "' BETWEEN Kalendereintrag.start AND Kalendereintrag.ende) OR ('" +
                    start + "' < Kalendereintrag.start AND '" + ende + "' > Kalendereintrag.ende))");

             while (rs.next()) {
            int ueberschneidungen = rs.getInt("count");
            System.out.println("Errechnete Überschneidungen: " + ueberschneidungen);
            System.out.println("Übergebene Kalender_ID: " + kalender_id);

            if(ueberschneidungen > 0)
            result = true;
        }




        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Kalendereintrag wird zurückgeben.
        return result;
    }

    /**
     *
     *
     * @param start
     * @param ende
     * @param kalender_id
     * @param eintrag_id
     * @return result Ergebnisvektor vom Typ Kalendereintrag
     */
    public boolean check_datum_available_at_update(Timestamp start, Timestamp ende, int kalender_id, int eintrag_id) {
        Connection con = DBConnection.connection();
        boolean result = false;

        try {
            Statement stmt = con.createStatement();

            System.out.println("Übergebene Eintrag_ID: " + eintrag_id);

            ResultSet rs = stmt.executeQuery(

            "SELECT COUNT(*) AS count FROM Kalendereintrag INNER JOIN Teilnahme ON Kalendereintrag.eintrag_id = Teilnahme.eintrag_id " +
                    "WHERE kalender_id=" + kalender_id + " AND Kalendereintrag.eintrag_id!=" + eintrag_id + " AND ( ('" +
                    start + "' BETWEEN Kalendereintrag.start AND Kalendereintrag.ende ) OR ('" +
                    ende + "' BETWEEN Kalendereintrag.start AND Kalendereintrag.ende) OR ('" +
                    start + "' < Kalendereintrag.start AND '" + ende + "' > Kalendereintrag.ende))");

             while (rs.next()) {
            int ueberschneidungen = rs.getInt("count");
            System.out.println("Errechnete Überschneidungen: " + ueberschneidungen);
            System.out.println("Übergebene Kalender_ID: " + kalender_id);

            if(ueberschneidungen > 0)
            result = true;
        }




        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Kalendereintrag wird zurückgeben.
        return result;



    }

    /**
     * Checkt, welcher Kategorie der entsprechende Kalender hat.
     *
     * @param x
     * @return true, falls
     */
    public boolean check_kalendereintrag_exists_bykategorie (Kategorie x) {
        int anzahl = 5;
        Connection con = DBConnection.connection();
        try {
            Statement stmt = con.createStatement();


            ResultSet rs = stmt.executeQuery(

                    "SELECT COUNT(*) AS count " +
                    "FROM Kalendereintrag " +
                    "WHERE kategorie_id=" + x.get_kategorie_id());

            // Für jeden Eintrag im Suchergebnis wird nun ein Kalendereintrag-Objekt ke erstellt.
            while (rs.next()) {
            anzahl = rs.getInt("count");
            }

        } catch (RemoteException ex) {
            Logger.getLogger(KategorieMapper.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Wenn es keine Einträge mehr gibt, gebe False zurück, ansonsten True
        if(anzahl==0)
            return false;
        else
            return true;
    }

    /**
     *Sucht alle Einladungen einer Person und gibt diese Kalendereinträge in einem
     *Vector zurück
     * @param person_id
     * @return result Ergebnisvektor vom Typ Kalendereintrag
     */
    public Vector<Kalendereintrag> find_all_einladungen_by_person(int person_id) {
        Connection con = DBConnection.connection();
        Vector result = new Vector();

        try {
            Statement stmt = con.createStatement();


            ResultSet rs = stmt.executeQuery(

                    "SELECT * " +
                    "FROM Kalendereintrag INNER JOIN Teilnahme ON Kalendereintrag.eintrag_id = Teilnahme.eintrag_id WHERE Teilnahme.kalender_id IS NULL " +
                    "AND Teilnahme.person_id=" + person_id);

            // Für jeden Eintrag im Suchergebnis wird nun ein Kalendereintrag-Objekt ke erstellt.
            while (rs.next()) {
                try {
                    Kalendereintrag ke = new KalendereintragImpl();
                    ke.set_beschreibung(rs.getString("beschreibung"));
                    ke.set_eintrag_id(rs.getInt("eintrag_id"));
                    ke.set_ende(rs.getTimestamp("ende"));
                    ke.set_erinnerung(rs.getInt("erinnerung"));
                    ke.set_erstelltvon(rs.getInt("person_id"));
                    ke.set_kalender_id(0); //Setzen wir hier in dem Fall nicht, da dieser keine Rolle spielt
                    ke.set_kategorie(rs.getInt("kategorie_id"));
                    ke.set_ort(rs.getString("ort"));
                    ke.set_start(rs.getTimestamp("start"));
                    result.addElement(ke);

                    // Wenn kein Titel vorhanden ist bleibt der String Titel des Vectors leer
                } catch (Exception e) {
                    e.printStackTrace();
                    //Bei einer evtl. Exception wird die while-Schleife abgebrochen.
                    break;
                }

            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Kalendereintrag wird zurückgeben.
        return result;

    }

    /**
     *Auslesen von zuweisungen welcher Kalender, welcher Person zugeordnet werden kann
     *
     * @param x,y,z
     * @return result Ergebnisvektor vom Typ Kalendereintrag
     */
    public void zuweisung_Kalender_Teilnahme(Kalendereintrag x, Kalender y, Person z) {/* Stellt durch Aufruf der connection() Methode der DBConnection-Klasse, die Verbindung zur Datenbank her. */
        Connection con = DBConnection.connection();

        try {
            // Leeres SQL-Statement stmt wird angelegt.
            Statement stmt = con.createStatement();

                   stmt.executeUpdate("UPDATE Teilnahme " +
                    "SET teilnahme=1, " +
                    "person_id=NULL, " +
                    "kalender_id=" + y.get_kalender_id() + " " +
                    "WHERE eintrag_id=" + x.get_eintrag_id() +
                    " AND person_id=" + z.get_person_id());

        } catch (SQLException e2) {
            e2.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
}

    /**
     *Die Kalendereinträge werden dem Reporter in einem Vector übergeben
     *
     * @param Vector
     * @return result Ergebnisvektor vom Typ Kalendereintrag
     */
    public Vector <Kalendereintrag> get_ausgabe_reporter(Kalender x, Kategorie y, Timestamp sta, Timestamp end) {
        Connection con = DBConnection.connection();
        Vector result = new Vector();

        // Kategorie ID und Kalender ID wird ausgelesen, da diese für die Abfrage nötig ist
        int kategorie_id =0;
        int kalender_id=0;

            try {
                kategorie_id = y.get_kategorie_id();
                kalender_id = x.get_kalender_id();
            } catch (RemoteException ex) {
                Logger.getLogger(KalendereintragMapper.class.getName()).log(Level.SEVERE, null, ex);
            }

        try {
            Statement stmt = con.createStatement();


            //Quickn Dirt fix
            String where = "";
            if(kategorie_id != 9999999)
                where = " AND kategorie_id=" + kategorie_id; // Die Abfrage kommt dazu, wenn nicht <alle> ausgewählt wurde

            System.out.println("DEBUG:");
            System.out.println(

                    "SELECT * " +
                    "FROM Kalendereintrag INNER JOIN Teilnahme ON Kalendereintrag.eintrag_id = Teilnahme.eintrag_id " +
                    "WHERE ( Kalendereintrag.start BETWEEN " +
                    "'" + sta + "'" +
                    " AND " +
                    "'" + end + "') " +
                    "AND Teilnahme.teilnahme=1 " +
                    "AND Teilnahme.kalender_id=" + kalender_id + where );

            ResultSet rs = stmt.executeQuery(

                    "SELECT * " +
                    "FROM Kalendereintrag INNER JOIN Teilnahme ON Kalendereintrag.eintrag_id = Teilnahme.eintrag_id " +
                    "WHERE ( Kalendereintrag.start BETWEEN " +
                    "'" + sta + "'" +
                    " AND " +
                    "'" + end + "') " +
                    "AND Teilnahme.teilnahme=1 " +
                    "AND Teilnahme.kalender_id=" + kalender_id + where );

          
            while (rs.next()) {
                try {
                    Kalendereintrag ke = new KalendereintragImpl();
                    ke.set_beschreibung(rs.getString("beschreibung"));
                    ke.set_eintrag_id(rs.getInt("eintrag_id"));
                    ke.set_ende(rs.getTimestamp("ende"));
                    ke.set_erinnerung(rs.getInt("erinnerung"));
                    ke.set_erstelltvon(rs.getInt("person_id"));
                    ke.set_kalender_id(0); //Setzen wir hier in dem Fall nicht, da dieser keine Rolle spielt
                    ke.set_kategorie(rs.getInt("kategorie_id"));
                    ke.set_ort(rs.getString("ort"));
                    ke.set_start(rs.getTimestamp("start"));
                    result.addElement(ke);

                    // Wenn kein Titel vorhanden ist bleibt der String Titel des Vectors leer
                } catch (Exception e) {
                    e.printStackTrace();
                    //Bei einer evtl. Exception wird die while-Schleife abgebrochen.
                    break;
                }

            }
        } catch (SQLException e2) {
            e2.printStackTrace();
        }
        // Ergebnisvektor vom Typ Kalendereintrag wird zurückgeben.
        return result;

    }
}
